<%- # the license inside this block applies to this file
# Copyright 2017 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-%>
<%= lines(autogen_notice(:go, pwd)) -%>
package google

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"google.golang.org/api/cloudresourcemanager/v1"
)
<% resource_name = product_ns + object.name -%>
<%
resource_uri = object.iam_policy.base_url || object.self_link_uri
parent_resource_name = object.iam_policy.parent_resource_attribute || object.name.underscore
resource_params = extract_identifiers(resource_uri.gsub('{{name}}', "{{#{parent_resource_name}}}"))
-%>

var <%= resource_name -%>IamSchema = map[string]*schema.Schema{
<% resource_params.each_with_index do |param, i| -%>
	"<%= param.underscore -%>": {
		Type:             schema.TypeString,
<% if ['project', 'zone', 'region', 'location'].include?(param) -%>
		Computed: true,
		Optional: true,
<% else -%>
		Required: true,
<% end # if ...include?(param) -%>
		ForceNew:         true,
<%# The last parameter can be used as a long name for IAM policies -%>
<% if i == resource_params.size - 1 -%>
<% if object.iam_policy.custom_diff_suppress.nil? -%>
		DiffSuppressFunc: compareSelfLinkOrResourceName,
<% else -%>
		DiffSuppressFunc: <%= resource_name -%>DiffSuppress,
<% end -%>
<% end # param == object.name -%>
	},
<% end # i == resource_params.size - 1 -%>
}

<% unless object.iam_policy.custom_diff_suppress.nil? -%>
<%= lines(compile(pwd + '/' + object.iam_policy.custom_diff_suppress)) -%>
<% end -%>

type <%= resource_name -%>IamUpdater struct {
<% resource_params.each do |param| -%>
	<%= param.camelize(:lower) -%> string
<% end # resource_params.each -%>
	d       TerraformResourceData
	Config  *Config
}

<% provider_default_values = ['project', 'location', 'region', 'zone'] -%>
func <%= resource_name -%>IamUpdaterProducer(d TerraformResourceData, config *Config) (ResourceIamUpdater, error) {
	values := make(map[string]string)

<% resource_params.each do |param| -%>
<% if provider_default_values.include?(param) -%>
	<%= param -%>, _ := get<%= param.capitalize -%>(d, config)
	if <%= param -%> != "" {
		if err := d.Set("<%= param -%>", <%= param -%>); err != nil {
			return nil, fmt.Errorf("Error setting <%= param -%>: %s", err)
		}
	}
	values["<%= param -%>"] = <%= param -%>

<% else -%>
	if v, ok := d.GetOk("<%= param.underscore -%>"); ok {
		values["<%= param -%>"] = v.(string)
	}

<% end # if provider_default_values.include? -%>
<% end # resource_params.each -%>
<% import_format = object.iam_policy.import_format || object.import_format -%>

	// We may have gotten either a long or short name, so attempt to parse long name if possible
	m, err := getImportIdQualifiers([]string{"<%= import_id_formats(import_format, object.identity, object.base_url).map{|s| format2regex s}.map{|s| s.gsub('<name>', "<#{parent_resource_name}>")}.join('","') -%>"}, d, config, d.Get("<%= parent_resource_name -%>").(string))
	if err != nil {
		return nil, err
	}

	for k, v := range m {
		values[k] = v
	}

	u := &<%= resource_name -%>IamUpdater{
<% resource_params.each do |param| -%>
		<%= param.camelize(:lower) -%>: values["<%= param -%>"],
<% end -%>
		d:       d,
		Config:  config,
	}

<%# Set all URL qualifiers in state so that we have consistent storage of needed fields -%>
<% resource_params.each_with_index do |param, i| -%>
<% if i == resource_params.size - 1 -%>
<% if param == 'project' -%>
	if err := d.Set("project", u.project); err != nil {
		return nil, fmt.Errorf("Error setting project: %s", err)
	}
<% else -%>
<%# Set the last parameter as the long name (unless it is project) -%>
	if err := d.Set("<%= parent_resource_name -%>", u.GetResourceId()); err != nil {
		return nil, fmt.Errorf("Error setting <%= parent_resource_name -%>: %s", err)
	}
<% end -%>
<% else -%>
	if err := d.Set("<%= param.underscore -%>", u.<%= param.camelize(:lower) -%>); err != nil {
		return nil, fmt.Errorf("Error setting <%= param.underscore -%>: %s", err)
	}
<% end -%>
<% end -%>

	return u, nil
}

func <%= resource_name -%>IdParseFunc(d *schema.ResourceData, config *Config) error {
	values := make(map[string]string)

<% resource_params.each do |param| -%>
<% if provider_default_values.include?(param) -%>
	<%= param -%>, _ := get<%= param.capitalize -%>(d, config)
	if <%= param -%> != "" {
		values["<%= param -%>"] = <%= param -%>
	}

<% end # if provider_default_values.include? -%>
<% end # resource_params.each -%>

	m, err := getImportIdQualifiers([]string{"<%= import_id_formats(import_format, object.identity, object.base_url).map{|s| format2regex s}.map{|s| s.gsub('<name>', "<#{parent_resource_name}>")}.join('","') -%>"}, d, config, d.Id())
	if err != nil {
		return err
	}

	for k, v := range m {
    values[k] = v
	}

	u := &<%= resource_name -%>IamUpdater{
<% resource_params.each do |param| -%>
		<%= param.camelize(:lower) -%>: values["<%= param -%>"],
<% end -%>
		d:       d,
		Config:  config,
	}
<% if resource_params.last == 'project' -%>
<%# Resource is only identified by project, so only set project -%>
	if err := d.Set("project", u.project); err != nil {
		return fmt.Errorf("Error setting project: %s", err)
	}
<% else -%>
<%# Set resource long name in state, this has all the information that we need to identify it -%>
	if err := d.Set("<%= parent_resource_name -%>", u.GetResourceId()); err != nil {
		return fmt.Errorf("Error setting <%= parent_resource_name -%>: %s", err)
	}
<% end -%>
	d.SetId(u.GetResourceId())
	return nil
}

func (u *<%= resource_name -%>IamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
	url, err := u.qualify<%= object.name -%>Url("<%= object.iam_policy.fetch_iam_policy_method -%>")
	if err != nil {
		return nil, err
	}

<% if resource_params.include?('project') -%>
	project, err := getProject(u.d, u.Config)
	if err != nil {
		return nil, err
	}
<% end -%>
	var obj map[string]interface{}
<% if object.iam_policy.iam_conditions_request_type == :QUERY_PARAM -%>
	url, err = addQueryParams(url, map[string]string{"optionsRequestedPolicyVersion": fmt.Sprintf("%d", <% if object.iam_policy.iam_policy_version -%> <%= object.iam_policy.iam_policy_version -%> <% else -%> iamPolicyVersion <% end -%>)})
	if err != nil {
		return nil, err
	}
<% elsif object.iam_policy.iam_conditions_request_type == :REQUEST_BODY -%>
	obj = map[string]interface{}{
		"options": map[string]interface{}{
			"requestedPolicyVersion": <% if object.iam_policy.iam_policy_version -%> <%= object.iam_policy.iam_policy_version -%> <% else -%> iamPolicyVersion <% end -%>,
		},
	}
<%  end -%>

	userAgent, err := generateUserAgentString(u.d, u.Config.userAgent)
	if err != nil {
		return nil, err
	}

	policy, err := sendRequest(u.Config, "<%= object.iam_policy.fetch_iam_policy_verb.to_s.upcase -%>", <% if resource_params.include?('project')  %>project<% else %>""<% end %>, url, userAgent, obj<%= object.error_retry_predicates ? ", " + object.error_retry_predicates.join(',') : "" -%>)
	if err != nil {
		return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err)
	}

	out := &cloudresourcemanager.Policy{}
	err = Convert(policy, out)
	if err != nil {
		return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err)
	}

	return out, nil
}

func (u *<%= resource_name -%>IamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error {
	json, err := ConvertToMap(policy)
	if err != nil {
		return err
	}

<% if object.iam_policy.iam_policy_version -%>
        // This is an override of the existing version that might have been set in the resource_iam_member|policy|binding code
        json["version"] = <%= object.iam_policy.iam_policy_version -%>
<% end -%>

<% if object.iam_policy.wrapped_policy_obj -%>
	obj := make(map[string]interface{})
	obj["policy"] = json
<% else -%>
	obj := json
<% end -%>

	url, err := u.qualify<%= object.name -%>Url("<%= object.iam_policy.set_iam_policy_method -%>")
	if err != nil {
		return err
	}
<% if resource_params.include?('project') -%>
	project, err := getProject(u.d, u.Config)
	if err != nil {
		return err
	}
<% end -%>

	userAgent, err := generateUserAgentString(u.d, u.Config.userAgent)
	if err != nil {
		return err
	}

	_, err = sendRequestWithTimeout(u.Config, "<%= object.iam_policy.set_iam_policy_verb.to_s.upcase -%>", <% if resource_params.include?('project')  %>project<% else %>""<% end %>, url, userAgent, obj, u.d.Timeout(schema.TimeoutCreate)<%= object.error_retry_predicates ? ", " + object.error_retry_predicates.join(',') : "" -%>)
	if err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err)
	}

	return nil
}

<% import_url = resource_uri.gsub(/({{)\%?(\w+)(}})/, '%s') -%>
<% string_qualifiers = extract_identifiers(resource_uri.gsub('{{name}}', "{{#{parent_resource_name}}}")).map{|param| "u.#{param.camelize(:lower)}"}.join(', ') -%>
func (u *<%= resource_name -%>IamUpdater) qualify<%= object.name -%>Url(methodIdentifier string) (string, error) {
	urlTemplate := fmt.Sprintf("{{<%= object.__product.name -%>BasePath}}%s<%= object.iam_policy.method_name_separator -%>%s", fmt.Sprintf("<%= import_url -%>", <%= string_qualifiers -%>), methodIdentifier)
  url, err := replaceVars(u.d, u.Config, urlTemplate)
  if err != nil {
      return "", err
  }
  return url, nil
}

func (u *<%= resource_name -%>IamUpdater) GetResourceId() string {
	return fmt.Sprintf("<%= import_url -%>", <%= string_qualifiers -%>)
}

func (u *<%= resource_name -%>IamUpdater) GetMutexKey() string {
	return fmt.Sprintf("iam-<%= product_ns.downcase -%>-<%= object.name.downcase -%>-%s", u.GetResourceId())
}

func (u *<%= resource_name -%>IamUpdater) DescribeResource() string {
	return fmt.Sprintf("<%= product_ns.downcase -%> <%= object.name.downcase -%> %q", u.GetResourceId())
}
